package io.sundial.hibernate;

import io.sundial.core.context.Context;
import io.sundial.core.lifecycle.Lifecycle;
import io.sundial.core.lifecycle.Stateful;
import io.sundial.core.lifecycle.exception.DestroyingException;
import io.sundial.core.lifecycle.exception.InitializingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

/**
 * Hibernate对象仓储超类
 *
 * @author Payne 646742615@qq.com
 * 2018/12/29 14:44
 */
public class SessionFactoryRepository extends Stateful implements Lifecycle {
    private SessionFactory sessionFactory;

    @Override
    protected void initializing(Context context) throws InitializingException {
        super.initializing(context);

        sessionFactory = context.get(SessionFactory.class, new SessionFactorySupplier());
    }

    @Override
    protected void destroying() throws DestroyingException {
        super.destroying();

        sessionFactory.close();
    }

    protected <R, E extends Exception> R doTransaction(TrxRTCall<R, E> call) throws E {
        Session sex = sessionFactory.openSession();
        Transaction trx = null;
        try {
            trx = sex.beginTransaction();
            R value = call.call(sex);
            trx.commit();
            return value;
        } catch (Exception e) {
            if (trx != null) {
                trx.rollback();
            }
            throw e;
        } finally {
            sex.close();
        }
    }

    protected <R> R doTransaction(TrxRNCall<R> call) {
        Session sex = sessionFactory.openSession();
        Transaction trx = null;
        try {
            trx = sex.beginTransaction();
            R value = call.call(sex);
            trx.commit();
            return value;
        } catch (RuntimeException e) {
            if (trx != null) {
                trx.rollback();
            }
            throw e;
        } finally {
            sex.close();
        }
    }

    protected <E extends Exception> void doTransaction(TrxVTCall<E> call) throws E {
        Session sex = sessionFactory.openSession();
        Transaction trx = null;
        try {
            trx = sex.beginTransaction();
            call.call(sex);
            trx.commit();
        } catch (Exception e) {
            if (trx != null) {
                trx.rollback();
            }
            throw e;
        } finally {
            sex.close();
        }
    }

    protected void doTransaction(TrxVNCall call) {
        Session sex = sessionFactory.openSession();
        Transaction trx = null;
        try {
            trx = sex.beginTransaction();
            call.call(sex);
            trx.commit();
        } catch (RuntimeException e) {
            if (trx != null) {
                trx.rollback();
            }
            throw e;
        } finally {
            sex.close();
        }
    }

    protected <R, E extends Exception> R doReadonly(TrxRTCall<R, E> call) throws E {
        Transaction trx = null;
        try (Session sex = sessionFactory.openSession()) {
            try {
                sex.setDefaultReadOnly(true);
                trx = sex.beginTransaction();
                return call.call(sex);
            } finally {
                if (trx != null) {
                    trx.rollback();
                }
            }
        }
    }

    protected <R> R doReadonly(TrxRNCall<R> call) {
        Transaction trx = null;
        try (Session sex = sessionFactory.openSession()) {
            try {
                sex.setDefaultReadOnly(true);
                trx = sex.beginTransaction();
                return call.call(sex);
            } finally {
                if (trx != null) {
                    trx.rollback();
                }
            }
        }
    }

    protected <E extends Exception> void doReadonly(TrxVTCall<E> call) throws E {
        Transaction trx = null;
        try (Session sex = sessionFactory.openSession()) {
            try {
                sex.setDefaultReadOnly(true);
                trx = sex.beginTransaction();
                call.call(sex);
            } finally {
                if (trx != null) {
                    trx.rollback();
                }
            }
        }
    }

    protected void doReadonly(TrxVNCall call) {
        Transaction trx = null;
        try (Session sex = sessionFactory.openSession()) {
            try {
                sex.setDefaultReadOnly(true);
                trx = sex.beginTransaction();
                call.call(sex);
            } finally {
                if (trx != null) {
                    trx.rollback();
                }
            }
        }
    }
}
